import { math } from 'cc';
import { EcsSystem, filter, IEntity } from 'db://pkg/@gamex/cc-ecs';
import { IMoveComponent, MoveComponent, MoveTowardType } from '../component/MoveComponent';
import { NodeComponent } from '../component/NodeComponent';

export class MoveSystem extends EcsSystem {
    private moveFilter = filter.all(MoveComponent);

    protected execute(dt: number) {
        this.query(this.moveFilter).forEach(entity => {
            const move = entity.getComponent(MoveComponent);
            this.handle(move, move.entity, dt);
        });
    }

    private handle(move: IMoveComponent, entity: IEntity, dt: number) {
        if (move.correctDelay > 0) {
            if (move.correctDelay < dt) {
                this.stage1(move, entity, move.correctDelay);
                dt -= move.correctDelay;
                move.correctDelay = 0;
            } else {
                this.stage1(move, entity, dt);
                move.correctDelay -= dt;
                dt = 0;
            }
        }

        if (move.correctDelay === 0 && dt > 0) {
            this.stage2(move, entity, dt);
        }
    }

    /**
     * 阶段1: 只移动
     * @param move 
     * @param entity 
     * @param dt 
     */
    private stage1(move: IMoveComponent, entity: IEntity, dt: number) {
        // 初速度
        const lastSpeed = move.speedInDelay;
        // 加速度
        move.speedInDelay += move.accelerationInDelay * dt;
        if (move.speedInDelay < move.minSpeedInDelay) {
            move.speedInDelay = move.minSpeedInDelay;
        }
        if (move.speedInDelay > move.maxSpeedInDelay) {
            move.speedInDelay = move.maxSpeedInDelay;
        }
        // 平均速度
        const speed = (lastSpeed + move.speedInDelay) / 2;
        this.run(move, entity, dt, speed);
    }

    /**
     * 阶段2: 移动+旋转
     * @param move 
     * @param entity 
     * @param dt 
     */
    private stage2(move: IMoveComponent, entity: IEntity, dt: number) {
        // 追踪角度
        if (move.target) {
            move.toward = MoveComponent.getTowardAngle(entity.getComponent(NodeComponent), move.target);
        }
        // 修正偏角
        if (move.turnAngle === 0) {
            move.deviation = 0;
        } else if (move.deviation > 0) {
            move.deviation -= move.turnAngle * dt;
            if (move.deviation < 0) move.deviation = 0;
        } else if (move.deviation < 0) {
            move.deviation += move.turnAngle * dt;
            if (move.deviation > 0) move.deviation = 0;
        }
        this.rotate(move, entity, dt);

        // 初速度
        const lastSpeed = move.speed;
        // 加速度
        move.speed += move.acceleration * dt;
        if (move.speed < move.minSpeed) {
            move.speed = move.minSpeed;
        }
        if (move.speed > move.maxSpeed) {
            move.speed = move.maxSpeed;
        }
        // 平均速度
        const speed = (lastSpeed + move.speed) / 2;
        this.run(move, entity, dt, speed);
    }

    private run(move: IMoveComponent, entity: IEntity, dt: number, speed: number) {
        // 移动距离
        const distance = speed * dt;
        // 设置节点位移
        if (move.angle === MoveTowardType.Right) {
            entity.getComponent(NodeComponent).addPosition(distance, 0);
        } else if (move.angle === MoveTowardType.Left) {
            entity.getComponent(NodeComponent).addPosition(-distance, 0);
        } else if (move.angle === MoveTowardType.Up) {
            entity.getComponent(NodeComponent).addPosition(0, distance);
        } else if (move.angle === MoveTowardType.Down) {
            entity.getComponent(NodeComponent).addPosition(0, -distance);
        } else {
            // 会有细微的误差
            const radian = math.toRadian(move.angle);
            const x = Math.cos(radian) * distance;
            const y = Math.sin(radian) * distance;
            entity.getComponent(NodeComponent).addPosition(x, y);
        }
        // 记录移动
        move.distance += distance;
    }

    private rotate(move: IMoveComponent, entity: IEntity, dt: number) {
        let addAngle = 0;
        if (move.deviation !== 0 || move.toward !== move.angle) {
            // 逻辑角度(朝向+偏航)
            const logicAngle = MoveComponent.getAngleFromNeg180to180(move.toward + move.deviation);
            // 角度差值
            const diffAngle = MoveComponent.getAngleFromNeg180to180(logicAngle - move.angle);
            // 差值限制(实际增加)
            addAngle = move.turnAngle === 0 ? diffAngle : MoveComponent.getAbsoluteValue(diffAngle, move.turnAngle * dt);
            // 实际角度
            move.angle += addAngle;

            // 设置节点角度
            if (move.openRotate) {
                entity.getComponent(NodeComponent).setAngle(move.angle);
            }
        }

        return addAngle;
    }
}

